package view;

import controller.AddSequenceListener;
import controller.PlayerControlListener;
import controller.WindowController;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import model.Instrument;
import view.tool.SequenceTool;
import view.tool.ToolType;

public class EditorPanel extends JPanel {

	public static final int MIN_HEIGHT = 116;

	private GUI gui;
	private InstrumentListPanel instrumentListPanel;
	private JScrollPane instrumentScrollPane;
	private InstrumentTrackSynchroniser scrollSynchroniser;
	private TrackListPanel trackListPanel;
	private JScrollPane trackScrollPane;
	private Point savedTrackListLocation;
	private LargeTrackPanel selectedTrackPanel;
	private JSlider seeker;

	public EditorPanel(GUI gui) {
		this.gui = gui;

		this.setOpaque(false);
		this.setLayout(new BorderLayout());

	//Seeker
		seeker = new JSlider();
		seeker.setUI(new SeekerUI(seeker));
		seeker.setBackground(new Color(0x666666));
		seeker.setPreferredSize(new Dimension(WindowController.MAX_SIZE.width, 8));
		seeker.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLACK));

	//Instrument Buttons
		instrumentListPanel = new InstrumentListPanel();
		instrumentScrollPane = new JScrollPane(instrumentListPanel,
			ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
			ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
		);
		instrumentScrollPane.setOpaque(false);
		instrumentScrollPane.setBorder(BorderFactory.createEmptyBorder());
		instrumentScrollPane.getViewport().setOpaque(false);

	//Track Panels
		selectedTrackPanel = null;

		trackListPanel = new TrackListPanel();
		trackScrollPane = new JScrollPane(trackListPanel,
			ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
			ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED
		);

		trackScrollPane.setBorder(
			BorderFactory.createMatteBorder(0, 1, 0, 0, new Color(0x666666))
		);
		trackScrollPane.setOpaque(false);
		trackScrollPane.getViewport().setOpaque(false);
		trackScrollPane.getViewport().addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent e) {
				JViewport viewport = (JViewport)e.getComponent();
				if(hasSelectedTrackPanel()) {
					selectedTrackPanel.stretch(viewport.getHeight());
				}
			}
		});

		//Synchronise scrolling between instruments and tracks.
		scrollSynchroniser = new InstrumentTrackSynchroniser();
		instrumentScrollPane.getViewport().addChangeListener(scrollSynchroniser);
		instrumentScrollPane.addMouseWheelListener(scrollSynchroniser);
		trackScrollPane.getVerticalScrollBar().addAdjustmentListener(scrollSynchroniser);

		this.savedTrackListLocation = trackScrollPane.getViewport().getViewPosition();

		this.add(seeker, BorderLayout.NORTH);
		this.add(instrumentScrollPane, BorderLayout.WEST);
		this.add(trackScrollPane, BorderLayout.CENTER);

		this.setMinimumSize(new Dimension(WindowController.MIN_SIZE.width, MIN_HEIGHT));
	}

	public JSlider getSeeker() {
		return seeker;
	}

	//Highlights the column at the given index.
	public void highlightColumn(int columnIndex) {
		if(this.hasSelectedTrackPanel()) {
			selectedTrackPanel.highlightColumn(columnIndex);
		}
		trackListPanel.highlightColumn(columnIndex);
	}

	public void showSelectedTrackPanel(TrackPanel trackPanel) {
		selectedTrackPanel = trackPanel.enlarge();
		savedTrackListLocation = trackScrollPane.getViewport().getViewPosition();

		//Stop synchronising vertical scrolls
		trackScrollPane.getVerticalScrollBar().removeAdjustmentListener(scrollSynchroniser);
		instrumentScrollPane.getViewport().removeChangeListener(scrollSynchroniser);

		trackScrollPane.setViewportView(selectedTrackPanel);
		trackScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
		trackScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

		//Alert selectedTrackPanel to resize itself and fill the area
		JViewport port = trackScrollPane.getViewport();
		for(ComponentListener cl : port.getComponentListeners()) {
			cl.componentResized(new ComponentEvent(port, ComponentEvent.COMPONENT_SHOWN));
		}
		this.revalidate();
	}

	public void hideSelectedTrackPanel() {
		//Replace this the track list
		trackScrollPane.setViewportView(trackListPanel);
		trackScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
		trackScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

		//Start synchronising scrolling with instruments again.
		trackScrollPane.getViewport().setViewPosition(savedTrackListLocation);
		trackScrollPane.getVerticalScrollBar().addAdjustmentListener(scrollSynchroniser);
		instrumentScrollPane.getViewport().addChangeListener(scrollSynchroniser);

		selectedTrackPanel = null;
		this.revalidate();
	}

	public boolean hasSelectedTrackPanel() {
		return selectedTrackPanel != null;
	}

	public void setAddSequenceListener(AddSequenceListener asl) {
		instrumentListPanel.setAddSequenceListener(asl);
	}

	public void addSequence(InstrumentButton instrumentButton, TrackPanel trackPanel) {
		//Apply listeners to switch between focus/un-focus modes for tracks
		SequenceSelectListener ssl = new SequenceSelectListener(trackPanel);
		instrumentButton.addItemListener(ssl);
		instrumentButton.addActionListener(ssl);

		this.instrumentListPanel.addInstrumentButton(instrumentButton);
		this.trackListPanel.addTrackPanel(trackPanel);

		this.revalidate();
	}

	public void removeSequence(int sequenceIndex) {
		this.instrumentListPanel.removeInstrumentButton(sequenceIndex);
		this.trackListPanel.removeTrackPanel(sequenceIndex);
		this.revalidate();
	}

	public void removeSequences(int startIndex, int endIndex) {
		for(int i = startIndex; i < endIndex; i++) {
			//Remove sequences from the top.
			this.removeSequence(startIndex);
		}
	}

	public void changeInstrument(int sequenceIndex, Instrument instrument) {
		this.instrumentListPanel.changeInstrumentIcon(sequenceIndex, instrument);
	}

	public void scrollToBottom() {
		instrumentListPanel.scrollRectToVisible(new Rectangle(0, instrumentListPanel.getHeight(), 1, 1));
	}

	public void setPlayerControlListener(PlayerControlListener pcl) {
		seeker.addChangeListener(pcl);
	}

	/**
	 * Synchronises scrolling on the track panel and the instrument panel to
	 * keep them aligned
	 */
	private class InstrumentTrackSynchroniser implements AdjustmentListener, ChangeListener, MouseWheelListener {

		@Override
		public void adjustmentValueChanged(AdjustmentEvent e) {
			if(e.getAdjustable().getOrientation() == Adjustable.VERTICAL) {
				//Scroll the instrument list with the track list.
				instrumentScrollPane.getViewport().setViewPosition(new Point(0, e.getValue()));
			}
		}

		@Override
		public void stateChanged(ChangeEvent e) {
			//Scroll the track list with the instrument list.
			JViewport port = trackScrollPane.getViewport();
			port.setViewPosition(new Point(
				port.getViewPosition().x,
				instrumentScrollPane.getViewport().getViewPosition().y)
			);
		}

		@Override
		public void mouseWheelMoved(MouseWheelEvent e) {
			//Scroll the track list with the instrument list.
			Adjustable adj = instrumentScrollPane.getVerticalScrollBar();
			if(e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
				int totalScrollAmount = e.getUnitsToScroll() * adj.getBlockIncrement();
				adj.setValue(adj.getValue() + totalScrollAmount);

				savedTrackListLocation.y += totalScrollAmount;
			}
		}

	}

	/**
	 * Switches between focus (novel) mode and un-focused (conventional) modes.
	 */
	private class SequenceSelectListener implements ItemListener, ActionListener {

		private TrackPanel trackPanel;

		private SequenceSelectListener(TrackPanel trackPanel) {
			this.trackPanel = trackPanel;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			Object obj = e.getSource();
			if(obj instanceof JToggleButton) {
				JToggleButton button = ((JToggleButton)obj);
				if(!button.isSelected()) {
					//No sequence selected, show effects tool.
					gui.setToolPanel(ToolType.EFFECTS);
				}
			}
		}

		@Override
		public void itemStateChanged(ItemEvent e) {
			if(e.getStateChange() == ItemEvent.SELECTED) {
				showSelectedTrackPanel(trackPanel);
				//Get sequence tool associated with this sequence.
				SequenceTool sequenceTool = ((InstrumentButton)e.getItemSelectable()).getSequenceTool();
				gui.getToolPanel().setSequenceTool(sequenceTool);
				gui.setToolPanel(ToolType.SEQUENCE);
			} else {
				hideSelectedTrackPanel();
			}
		}

	}

}
